import json
import traceback
from functools import lru_cache
from typing import Any, Dict, List, Optional

import redis
import redis.asyncio as redis_async

from utils.base_utils import get_config, logger


@lru_cache(maxsize=1)
def get_redis_pool():
    redis_config = get_config("redis")
    return redis.ConnectionPool(
        host=redis_config.get("host"),
        port=redis_config.get("port"),
        db=0,
        password=redis_config.get("password"),
        decode_responses=True,
        max_connections=20,
        socket_connect_timeout=5,
        socket_timeout=5,
        retry_on_timeout=True,
    )


def get_redis_client():
    return redis.StrictRedis(connection_pool=get_redis_pool())


@lru_cache(maxsize=1)
def get_async_redis_pool():
    redis_config = get_config("redis") or {}
    return redis_async.ConnectionPool(
        host=redis_config.get("host", "localhost"),
        port=redis_config.get("port", 6379),
        db=0,
        password=redis_config.get("password"),
        decode_responses=True,
        max_connections=20,
        socket_connect_timeout=5,
        socket_timeout=5,
    )


def get_async_redis_client():
    return redis_async.StrictRedis(connection_pool=get_async_redis_pool())


def _serialize_value(value) -> str:
    if value is None:
        return ""
    if hasattr(value, "to_dict"):
        value = value.to_dict()
    elif hasattr(value, "dict"):
        value = value.dict()
    if isinstance(value, Dict) or isinstance(value, List):
        return json.dumps(value, ensure_ascii=False)
    elif isinstance(value, str):
        value = value
    return value


class RedisPipe:
    def __init__(self):
        self.client = get_redis_client()

    def set(self, key: str, value, expire: int = 600) -> bool:
        if not key:
            logger.error("Redis set failed: key is empty or None")
            return False
        try:
            serialized_value = _serialize_value(value)
            logger.info(
                f"Redis setting key:{key}, value type:{type(value).__name__}, serialized length:{len(serialized_value)}"
            )
            self.client.set(key, serialized_value, ex=expire)
            return True
        except Exception as e:
            logger.error(
                f"Redis set error: {str(e)}, Traceback: {traceback.format_exc()}"
            )
            return False

    def get(self, key, load_as_dict: bool = False):
        try:
            result = self.client.get(key)
            logger.info(f"Redis getting key:{key}, exists:{result is not None}")
            if not result:
                return None
            if load_as_dict:
                try:
                    return json.loads(result)
                except json.JSONDecodeError:
                    logger.warning(
                        f"Failed to decode JSON for key:{key}, returning original string"
                    )
                    return result
            return result
        except Exception as e:
            logger.error(
                f"Redis get error: {str(e)}, Traceback: {traceback.format_exc()}"
            )
            return None

    def delete(self, key: str) -> bool:
        if not key:
            logger.error("Redis delete failed: key is empty or None")
            return False
        try:
            result = self.client.delete(key)
            logger.info(f"Redis deleting key:{key}, deleted_count:{result}")
            return result > 0
        except Exception as e:
            logger.error(
                f"Redis delete error: {str(e)}, Traceback: {traceback.format_exc()}"
            )
            return False

    def push(self, key: str, value: str, expire: int = 600) -> bool:
        if not key:
            logger.error("Redis push failed: key is empty or None")
            return False
        try:
            serialized_value = self._serialize_value(value)
            logger.info(
                f"Redis pushing key:{key}, value length:{len(serialized_value)}"
            )
            self.client.lpush(key, serialized_value)
            self.client.expire(key, expire)
            return True
        except Exception as e:
            logger.error(
                f"Redis push error: {str(e)}, Traceback: {traceback.format_exc()}"
            )
            return False

    def pop(self, key: str, load_as_dict: bool = False) -> str:
        if not key:
            logger.error("Redis pop failed: key is empty or None")
            return None
        try:
            logger.info(f"Redis popping key:{key}")
            value = self.client.rpop(key)
            if value is None:
                return None
            logger.info(f"Redis popped key:{key}, value length:{len(value)}")
            if load_as_dict:
                try:
                    return json.loads(value)
                except Exception:
                    logger.warning(f"Failed to decode JSON for popped key:{key}")
            return value
        except Exception as e:
            logger.error(
                f"Redis pop error: {str(e)}, Traceback: {traceback.format_exc()}"
            )
            return None

    def exists(self, key: str) -> bool:
        if not key:
            logger.error("Redis exists failed: key is empty or None")
            return False
        try:
            return bool(self.client.exists(key))
        except Exception as e:
            logger.error(
                f"Redis exists error: {str(e)}, Traceback: {traceback.format_exc()}"
            )
            return False


class AsyncRedisPipe:
    def __init__(self):
        self.client = get_async_redis_client()

    async def set(self, key: str, value: Any, expire: int = 600) -> bool:
        if not key:
            logger.error("Async Redis set failed: key is empty or None")
            return False
        try:
            serialized_value = _serialize_value(value)
            logger.info(
                f"Async Redis setting key:{key}, value type:{type(value).__name__}, serialized length:{len(serialized_value)}"
            )
            await self.client.set(key, serialized_value, ex=expire)
            return True
        except Exception as e:
            logger.error(
                f"Async Redis set error: {str(e)}, Traceback: {traceback.format_exc()}"
            )
            return False

    async def get(self, key: str, load_as_dict: bool = False) -> Optional[Any]:
        try:
            result = await self.client.get(key)
            logger.debug(f"Async Redis getting key:{key}, exists:{result is not None}")
            if not result:
                return None
            if load_as_dict:
                try:
                    return json.loads(result)
                except json.JSONDecodeError:
                    logger.warning(
                        f"Failed to decode JSON for key:{key}, returning original string"
                    )
            return result
        except Exception as e:
            logger.error(
                f"Async Redis get error: {str(e)}, Traceback: {traceback.format_exc()}"
            )
            return None

    async def delete(self, key: str) -> bool:
        if not key:
            logger.error("Async Redis delete failed: key is empty or None")
            return False
        try:
            result = await self.client.delete(key)
            logger.info(f"Async Redis deleting key:{key}, deleted_count:{result}")
            return result > 0
        except Exception as e:
            logger.error(
                f"Async Redis delete error: {str(e)}, Traceback: {traceback.format_exc()}"
            )
            return False

    async def push(self, key: str, value: Any, expire: int = 600) -> bool:
        if not key:
            logger.error("Async Redis push failed: key is empty or None")
            return False
        try:
            serialized_value = _serialize_value(value)
            logger.info(
                f"Async Redis pushing key:{key}, value length:{len(serialized_value)}"
            )
            await self.client.lpush(key, serialized_value)
            await self.client.expire(key, expire)
            return True
        except Exception as e:
            logger.error(
                f"Async Redis push error: {str(e)}, Traceback: {traceback.format_exc()}"
            )
            return False

    async def pop(self, key: str, load_as_dict: bool = False) -> Optional[Any]:
        if not key:
            logger.error("Async Redis pop failed: key is empty or None")
            return None
        try:
            logger.info(f"Async Redis popping key:{key}")
            value = await self.client.rpop(key)
            if value is None:
                logger.info(f"Async Redis popped key:{key}, value: (None)")
                return None
            logger.info(f"Async Redis popped key:{key}, value length:{len(value)}")
            if load_as_dict:
                try:
                    return json.loads(value)
                except Exception as e:
                    logger.warning(
                        f"Failed to decode JSON for popped key:{key}, error: {e}"
                    )
            return value
        except Exception as e:
            logger.error(
                f"Async Redis pop error: {str(e)}, Traceback: {traceback.format_exc()}"
            )
            return None

    async def exists(self, key: str) -> bool:
        if not key:
            logger.error("Async Redis exists failed: key is empty or None")
            return False
        try:
            return bool(await self.client.exists(key))
        except Exception as e:
            logger.error(
                f"Async Redis exists error: {str(e)}, Traceback: {traceback.format_exc()}"
            )
            return False

    async def __aenter__(self):
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        await self.client.close()


redis_pipe = RedisPipe()
async_redis_pipe = AsyncRedisPipe()


if __name__ == "__main__":
    # 测试 Redis 的增删改查功能
    print("开始测试 Redis 客户端...")

    # 测试键名
    test_key = "test_redis_key"
    test_key_dict = "test_redis_key_dict"

    # 1. 测试设置值 (set)
    print("\n1. 测试设置值 (set)")
    # 测试字符串值
    set_result_str = redis_pipe.set(test_key, "Hello Redis")
    print(f"设置字符串值结果: {set_result_str}")

    # 测试字典值
    test_dict = {"name": "RedisTest", "version": 1.0, "active": True}
    set_result_dict = redis_pipe.set(test_key_dict, test_dict)
    print(f"设置字典值结果: {set_result_dict}")

    # 2. 测试获取值 (get)
    print("\n2. 测试获取值 (get)")
    # 测试获取字符串值
    get_result_str = redis_pipe.get(test_key)
    print(f"获取字符串值结果: {get_result_str}")

    # 测试获取字典值（不解析为字典）
    get_result_dict_raw = redis_pipe.get(test_key_dict)
    print(
        f"获取字典值（原始）结果: {get_result_dict_raw}, 类型: {type(get_result_dict_raw).__name__}"
    )

    # 测试获取字典值（解析为字典）
    get_result_dict_parsed = redis_pipe.get(test_key_dict, load_as_dict=True)
    print(
        f"获取字典值（解析后）结果: {get_result_dict_parsed}, 类型: {type(get_result_dict_parsed).__name__}"
    )

    # 3. 测试更新值
    print("\n3. 测试更新值")
    update_result = redis_pipe.set(test_key, "Hello Updated Redis")
    print(f"更新值结果: {update_result}")
    updated_value = redis_pipe.get(test_key)
    print(f"更新后的值: {updated_value}")

    # 4. 测试删除值 (delete)
    print("\n4. 测试删除值 (delete)")
    delete_result_str = redis_pipe.delete(test_key)
    print(f"删除字符串值结果: {delete_result_str}")
    delete_result_dict = redis_pipe.delete(test_key_dict)
    print(f"删除字典值结果: {delete_result_dict}")

    # 5. 验证删除结果
    print("\n5. 验证删除结果")
    after_delete_str = redis_pipe.get(test_key)
    print(f"删除后的字符串值: {after_delete_str}")
    after_delete_dict = redis_pipe.get(test_key_dict)
    print(f"删除后的字典值: {after_delete_dict}")

    print("\nRedis 客户端测试完成!")

    async def example():
        await async_redis_pipe.set(
            "user:123", {"name": "Alice", "age": 30}, expire=3600
        )
        user = await async_redis_pipe.get("user:123", load_as_dict=True)
        print(user)  # {'name': 'Alice', 'age': 30}

        await async_redis_pipe.push("queue:tasks", {"task": "send_email"}, expire=600)
        task = await async_redis_pipe.pop("queue:tasks", load_as_dict=True)
        print(task)  # {'task': 'send_email'}

        exists = await async_redis_pipe.exists("user:123")
        print(exists)  # True

        deleted = await async_redis_pipe.delete("user:123")
        print(deleted)  # True
